/* 
 *  Syntelos XB
 *  Copyright (C) 2006 John Pritchard.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation; either version 2 of
 *  the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307 USA
 */

package syntelos.xb;

/** 
 * <p> Abstract base class for all elements. </p>
 * 
 * @author jdp
 */
public abstract class Base 
    extends syntelos.iou.dom.impl.Element
    implements CF,
               XB
{
    public final static syntelos.iou.dom.Name ATTRNAME_ID = new syntelos.iou.dom.impl.Name(ATTRIBUTE_NODE,STR_NIL,"id");

    /**
     * <p> A basic attribute node for XB. </p>
     */
    public abstract static class Attribute
        extends syntelos.iou.dom.impl.Attr
        implements CF,
                   XB
    {

        /**
         * <p> A numeric attribute. </p>
         */
        public abstract static class Operand
            extends syntelos.xb.Base.Attribute
        {
            /**
             * <p> A variable reference attribute. </p>
             */
            public abstract static class Var
                extends syntelos.xb.Base.Attribute.Operand
            {

                public Var(org.w3c.dom.Document doc, syntelos.iou.dom.Name type){
                    super(doc,type);
                }

                public syntelos.iou.dom.Node decompile(){
                    this.getOperandVar();
                    return this;
                }
                public syntelos.iou.dom.Node compile(){
                    this.getOperandVar();
                    return this;
                }
                public syntelos.xb.at.Variable getOperandVar(){

                    return (syntelos.xb.at.Variable)this.getData(syntelos.xb.IO.Data.VAR,this);
                }
                public void setOperand(syntelos.xb.at.Variable var){

                    this.setData(var,this);
                }
            }


            public Operand(org.w3c.dom.Document doc, syntelos.iou.dom.Name type){
                super(doc,type);
            }

            public int getOperand(){
                java.lang.Integer number = (java.lang.Integer)this.getData(syntelos.xb.IO.Data.INTEGER,this);
                if (null != number)
                    return number.intValue();
                else
                    throw new syntelos.xb.Error.State("Missing operand.");
            }
            public void setOperand(int value){
                java.lang.Integer number = new java.lang.Integer(value);
                this.setData(number,this);
            }
        }

        /**
         * <p> A name attribute. </p>
         */
        public abstract static class Name 
            extends syntelos.xb.Base.Attribute
        {

            public Name(org.w3c.dom.Document doc, syntelos.iou.dom.Name name){
                super(doc,name);
            }

            public CF.Constants.Utf8 getAttributeNameConst(){
                return (CF.Constants.Utf8)this.getData(IO.Data.CONST_UTF8,this);
            }
            public java.lang.String getAttributeNameConstString(){
                CF.Constants.Utf8 name = this.getAttributeNameConst();
                if (null != name)
                    return name.getString();
                else
                    throw new Error.State("Missing operand");
            }
            public int getAttributeName(){
                CF.Constants.Utf8 name = this.getAttributeNameConst();
                if (null != name)
                    return name.getIndex();
                else
                    throw new Error.State("Missing operand");
            }
            public void setAttributeName(int index){
                CF.Constants constants = this.getConstants();
                CF.Constants.Utf8 name = constants.getConstUtf8(index);
                this.setData(name,this);
            }
            public void setAttributeName(CF.Constants.Utf8 name){

                this.setData(name,this);
            }
            public void setAttributeName(String value){
                CF.Constants constants = this.getConstants();
                CF.Constants.Utf8 name = constants.constUtf8(value);
                this.setData(name,this);
            }
            public void readCF(IO.Uri src, CF.Reader reader)
                throws java.io.IOException
            {
                int name_index = reader.readUnsignedShort();
                this.setAttributeName(name_index);
            }
            public void writeCF(IO.Uri dst, CF.Writer writer)
                throws java.io.IOException
            {
                int name_index = this.getAttributeName();
                writer.writeShort(name_index);
            }
            public syntelos.iou.dom.Node compile(){
                this.getAttributeNameConst();
                return this;
            }
            public syntelos.iou.dom.Node decompile(){
                this.getAttributeNameConst();
                return this;
            }
        }


        public Attribute(org.w3c.dom.Document doc, syntelos.iou.dom.Name name){
            super(doc,name);
        }
        /**
         * @see syntelos.iou.dom.CharacterData$User
         */
        public final java.lang.Object getData(syntelos.iou.dom.CharacterData source, 
                                              int type, 
                                              java.lang.Object value)
        {
            return IO.Data.DT2Object(this,type,value);
        }
        /**
         * @see syntelos.iou.dom.CharacterData$User
         */
        public final int typeOf(java.lang.Object value)
        {
            return IO.Data.DT4Object(value);
        }
        /**
         * @see syntelos.iou.dom.CharacterData$User
         */
        public final int typeInteger(){
            return IO.Data.INTEGER;
        }
        /**
         * @see syntelos.iou.dom.CharacterData$User
         */
        public final boolean isTypeAny(syntelos.iou.dom.CharacterData source, int type){
            return false;
        }
        public syntelos.xb.CF.Constants getConstants(){
            return this.getDeclaringClass().getConstants();
        }
        public syntelos.xb.Class getDeclaringClass(){
            org.w3c.dom.Document document = this.getOwnerDocument();
            if (null == document)
                throw new Error.State("Missing document node.");
            else {
                org.w3c.dom.Element element = document.getDocumentElement();
                if (element instanceof syntelos.xb.Class)
                    return (syntelos.xb.Class)element;
                else
                    throw new Error.State("Missing document element 'class'.");
            }
        }
        public syntelos.xb.at.LocalVariableTable getAttributeLocalVariableTable(){
            throw new Error.State("unavailable");
        }
        public syntelos.xb.at.Code getAttributeCode(){
            throw new Error.State("unavailable");
        }
        public syntelos.iou.dom.Node compile(){
            return this;
        }
        public syntelos.iou.dom.Node decompile(){
            return this;
        }
    }

    
    public Base(org.w3c.dom.Document doc, syntelos.iou.dom.Name name){
        super(doc,name);
    }
    /**
     * Ctor for {@link Op}
     */
    protected Base(syntelos.iou.dom.Name name){
        super(DOC_NIL,name);
    }

    /**
     * @see syntelos.iou.dom.CharacterData$User
     */
    public final java.lang.Object getData(syntelos.iou.dom.CharacterData source, 
                                          int type, 
                                          java.lang.Object value)
    {
        return IO.Data.DT2Object(this,type,value);
    }
    /**
     * @see syntelos.iou.dom.CharacterData$User
     */
    public final int typeOf(java.lang.Object value)
    {
        return IO.Data.DT4Object(value);
    }
    /**
     * @see syntelos.iou.dom.CharacterData$User
     */
    public final int typeInteger(){
        return IO.Data.INTEGER;
    }
    /**
     * @see syntelos.iou.dom.CharacterData$User
     */
    public final boolean isTypeAny(syntelos.iou.dom.CharacterData source, int type){
        return false;
    }

    public syntelos.xb.CF.Constants getConstants(){
        return this.getDeclaringClass().getConstants();
    }
    public syntelos.xb.Method getMethod(){
        org.w3c.dom.Node parent = this.getParentNode();
        while (null != parent){
            if (parent instanceof Method)
                return (Method)parent;
            else
                parent = parent.getParentNode();
        }
        throw new Error.Bug("unavailable");
    }
    /**
     * <p> Used by {@link IO$Data}. </p>
     */
    public syntelos.xb.at.LocalVariableTable getAttributeLocalVariableTable(){
        return this.getMethod().getAttributeLocalVariableTable();
    }
    /**
     * <p> Used by {@link IO$Data}. </p>
     */
    public syntelos.xb.at.Code getAttributeCode(){
        return this.getMethod().getAttributeCode();
    }
    public syntelos.xb.op.Code getOpCode(){
        return this.getAttributeCode().getOpCode();
    }
    public syntelos.xb.Class getDeclaringClass(){
        org.w3c.dom.Document document = this.getOwnerDocument();
        if (null == document)
            throw new Error.State("Missing document node.");
        else {
            org.w3c.dom.Element element = document.getDocumentElement();
            if (element instanceof syntelos.xb.Class)
                return (syntelos.xb.Class)element;
            else
                throw new Error.State("Missing document element 'class'.");
        }
    }
    /**
     * Compilation event called before write process to finalize
     * state.  The state after decompile should be functionally
     * equivalent (logically identical) to the state after compile.
     */
    public syntelos.iou.dom.Node compile(){
        //
        return this;
    }
    /**
     * Decompilation event called after read process to initialize
     * state.  The state after decompile should be functionally
     * equivalent (logically identical) to the state after compile.
     */
    public syntelos.iou.dom.Node decompile(){
        //
        return this;
    }
    public final syntelos.iou.dom.Attr getIdAttr(){
        syntelos.iou.dom.Attr attr = this.getAttributeNode2(ATTRNAME_ID.getLocalname());
        if (null == attr){
            org.w3c.dom.Document doc = this.getOwnerDocument();
            attr = new syntelos.iou.dom.impl.Attr(doc,ATTRNAME_ID);
            this.setAttributeNode(attr);
        }
        return attr;
    }
    public final int getId(){
        syntelos.iou.dom.Attr attr = this.getIdAttr();
        java.lang.Integer number = (java.lang.Integer)attr.getData(syntelos.xb.IO.Data.INTEGER,this);
        if (null != number)
            return number.intValue();
        else
            return 0;
    }
    public void setId(int value){
        syntelos.iou.dom.Attr attr = this.getIdAttr();
        java.lang.Integer number = new java.lang.Integer(value);
        attr.setData(number,this);
    }

    public abstract void readCF(IO.Uri src, CF.Reader reader)
        throws java.io.IOException;

    public abstract void writeCF(IO.Uri dst, CF.Writer writer)
        throws java.io.IOException;

    /**
     * @param another Instance of {@link Base} exclusively, both this
     * and that must have ID attributes with integer values.
     */
    public /*(todo:drop)*/ final /*(tmp:final)*/ int compareTo(java.lang.Object another){
        if (another instanceof Base){
            Base that = (Base)another;
            int this_id = this.getId();
            int that_id = that.getId();
            if (this_id < that_id)
                return -1;
            else if (this_id == that_id)
                return 0;
            else
                return +1;
        }
        else if (null == another)
            throw new Error.Bug("Null object comparison");
        else
            throw new Error.Bug("Invalid comparison with "+another.getClass().toString());
    }

}
